#coding=utf-8
# Create your views here.
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response
from release.citylife.models import User, DoubanSubject, Job, Bid, WorkingPosition, EducationRecord
#from douban.service import DoubanService
#from douban.client import OAuthClient
import urllib
from xmlnode import XMLNode
from django.core.exceptions import ObjectDoesNotExist
from django import forms
import StringIO
from PIL import Image
import hashlib
import os
from django.core.files import File
import lxml.html as H
import time
from facilities import *
from django.forms import ModelForm

import logging
logging.basicConfig()
log = logging.getLogger(__name__)
formatter = logging.Formatter('%(asctime)s, %(filename)s, %(lineno)d: %(message)s')
s_hdlr = logging.StreamHandler()
s_hdlr.setFormatter(formatter)
f_hdlr = logging.FileHandler('media/citylife/log.txt')
f_hdlr.setFormatter(formatter)
log.addHandler(s_hdlr)
log.addHandler(f_hdlr)
log.setLevel(logging.INFO)

class GV:
    MATCH_DIMENTION = 200
    PAGE_LENGTH = 10
    SPIDER_NUMBER = 15
    MAX_REC_FRIEND_NUM = 30

def home(request):
    return render_to_response('home.html')

def initiate(request):
    populate_db()
    request.session['uid'] = None
    return HttpResponseRedirectAfter('/citylife/home/', 'initiated..')

def save_db():
    f = file('media/citylife/user.dat', 'w')
    for user in User.objects.all():
        print >> f, user.id, user.name, user.douban_subject_ids
    f.close()

    f = file('media/citylife/douban_subject.dat', 'w')
    for subject in DoubanSubject.objects.all():
        f.subject.id, subject.title, subject.url, subject.tags
    f.close()

def test(request):
    populate_db()
    update()
    #calCoordination()
    #friend_recommend()
    #book_recommend()
    #group_recommend()
    
    #users = User.objects.all()
    #names = dict([(user.id, user.name) for user in users])
    #return render_to_response('test.html', {'users': [(user.name, ','.join([names[id] for id in user.rec_friend_ids])) for user in User.objects.all()]})
    return HttpResponse('done')

##### match * #####
def importSubjects(user, url):
    """
    add the douban subjects in page [url] into db 
    """
    log.info('importSubjects(%s, %s)' % (user.name, url))

    
    url_opened = False

    ##### d * #####
    #url = 'http://book.douban.com/list/2362230/collect?start=0&status=do'
    ##### d #####

    for i in range(5):
        try:
            html = urllib.urlopen(url)
            if html:
                html = html.read().decode('utf8', 'ignore')
                url_opened = True
                break
        except Exception, e:
            log.error('urlopen failed, url=%s, error=%s', url, e)
            time.sleep(1)

    if (not url_opened):
        return 0

    doc = H.document_fromstring(html)
    uls = doc.xpath('//div[contains(@class, "article")]/ul[@class="collect tlst"]')
    
    for ul in uls:
        url = ul.xpath('li/h3/a/@href')[0]
        title = ul.xpath('li/h3/a/text()')[0]
        
        subject, created = DoubanSubject.objects.get_or_create(url = url)
        subject.title = title.encode('utf8', 'ignore')
        subject.url = url
        subject.tags = Fenci.fenci(subject.title)
        subject.save()

        if subject.id not in user.douban_subject_ids:
            user.douban_subject_ids.append(subject.id)
    
    user.save()
    return len(uls)

class DoubanSubjectCrawl(ThreadPool):
    def __init__(self, paras):
        Fenci.initiate()
        ThreadPool.__init__(self, self.spider, GV.SPIDER_NUMBER, paras)

    def spider(self, para):
        importSubjects(user = para[0], url = para[1])

def update():
    log.info('upload()')
    """
    update the info of [user] from douban
    """
    import thread,threading

    paras = []
    users = User.objects.all()
    for user in users:
        if user.updated:
            continue

        cat1s = ['book']
        cat2s = ['collect','wish','do']
        for cat1 in cat1s:
            for cat2 in cat2s:
                page_index = 0
                url = 'http://%s.douban.com/list/%s/%s' % (cat1, user.douban_id, cat2)
                for i in range(5):
                    paras.append((user, url))
                    url = 'http://%s.douban.com/list/%s/collect?start=%d&status=%s'%(cat1, user.douban_id, page_index*15, cat2)
                    page_index += 1

    dbsc = DoubanSubjectCrawl(paras)
    dbsc.run()

    for user in users:
        user.updated = 1
        user.save()

def getTopTags():
    top_tags = {}
    for subject in DoubanSubject.objects.all():
        for tag in subject.tags:
            top_tags[tag] = top_tags.setdefault(tag, 0) + 1
    top_tags = sorted(top_tags.items(), key = lambda e: e[1], reverse = True)[:GV.MATCH_DIMENTION]
    top_tags = [e[0] for e in top_tags]
    safe_print(top_tags)
    return top_tags
    

def calCoordination():
    """
    calculate the tag coordination of each user
    user0.tagCoord[tag0] = number of user.subjects that contains tag0
    """
    #top_tags = [s.decode('utf8') for s in file('media/citylife/top_tags.txt', 'r').read().split(';')]
    #GV.MATCH_DIMENTION = len(top_tags)
    top_tags = getTopTags()

    f = file('media/citylife/test.txt', 'w')
    for user in User.objects.all():
        top_tag_counts = {}
        for subject_id in user.douban_subject_ids:
            subject = DoubanSubject.objects.get(id = subject_id)

            for tag in subject.tags:
                if tag in top_tags:
                    top_tag_counts[tag] = top_tag_counts.setdefault(tag, 1) + 1

        user.top_tags = top_tag_counts.keys()
        user.top_tag_counts = top_tag_counts.values()

        tagCoord = []

        for top_tag in top_tags:
            tagCoord.append(top_tag_counts.get(top_tag, 0))

        user.tagCoord = tagCoord
        user.save()
        
def user_distance(user1, user2):
    c1, c2 = user1.tagCoord, user2.tagCoord
    return sum([abs(c1[i]-c2[i]) for i in range(len(c1))])

def friend_recommend():
    """
    update the [rec_friend_ids] of each user. First, compute the distance between each pair of users. Then, sorted the distances descending order. The [MATCH_THRESHOLD_RATIO*len(users)]-th distance is used as a threshold. Two users are matched if their distance is less than the threshold.
    """
    log.info('match()')

    users = User.objects.all()

    uids = [user.id for user in users]
    distance_mat = [[-1 for i in range(len(users))] for j in range(len(users))]
    for u1 in range(len(users)):
        for u2 in range(u1 + 1, len(users)):
            user1, user2 = users[u1], users[u2]
            distance_mat[u1][u2] = distance_mat[u2][u1] = user_distance(user1, user2)
    
    for u1 in range(len(users)):
        user = users[u1]
        rec_friend_ids = [e[0] for e in sorted([(uids[u2], distance_mat[u1][u2]) for u2 in range(len(users))], key = lambda e:e[1])][:GV.MAX_REC_FRIEND_NUM]
        user.rec_friend_ids = rec_friend_ids
        user.save()

def subjects_recommendation(user):
    from slope_one import SlopeOne

    userdata = {}
    for rec_friend_id in user.rec_friend_ids + user.id:
        userdata[rec_friend_id] = {}
        rec_friend = User.objects.get(id = rec_friend_id)
        for subject_id in rec_friend.subject_ids:
            userdata[rec_friend_id][subject_id] = 1

    s = SlopeOne()
    s.update(userdata)
    predicated_rating = predict(userdata[user.id])
    user.rec_subject_ids = sorted(predicated_rating.items(), key = lambda e:e[1], reverse = True)[:GV.MAX_REC_SUBJECTS_NUM]
    user.save()

def groups_recommendation(user):
    from slope_one import SlopeOne

    userdata = {}
    for rec_friend_id in user.rec_friend_ids + user.id:
        userdata[rec_friend_id] = {}
        rec_friend = User.objects.get(id = rec_friend_id)
        for group_id in rec_friend.group_ids:
            userdata[rec_friend_id][group_id] = 1

    s = SlopeOne()
    s.update(userdata)
    predicated_rating = predict(userdata[user.id])
    user.rec_group_ids = sorted(predicated_rating.items(), key = lambda e:e[1], reverse = True)[:GV.MAX_REC_SUBJECTS_NUM]
    user.save()
##### match #####


def populate_db():
    #for user in User.objects.all():
    #    user.delete()    

    #for subject in DoubanSubject.objects.all():
    #    subject.delete()

    data_xml = file('media/citylife/users1.xml').read()
    node = XMLNode.parseXML(data_xml)

    for user in node.user:
        name = user.elementText
        _user, created = User.objects.get_or_create(name = name)
        if created:
            _user.name = name
            _user.douban_id = name
            _user.save()

class LoginForm(ModelForm):
    class Meta:
        model = User
        fields = ('name', 'psd') 

def login(request):
    if request.method == 'POST':  
        form = LoginForm(request.POST)
        if form.is_valid():        
            name = form.cleaned_data.get('name')
            psd = form.cleaned_data.get('psd')
            try:
                user = User.objects.get(name = name, psd = psd)
                request.session['uid'] = user.id
                log.info('user.id=%s', user.id)
                return HttpResponseRedirectAfter('/citylife/home/', 'login successed. jump to home')
            except ObjectDoesNotExist:
                return HttpResponseRedirectAfter('/citylife/login/', 'wrong name or password')
    else:  
        form = LoginForm()
    
    return render_to_response('login.html', dict(form = form))

##### profile * #####

##### experience * #####
class BasicInformationForm(ModelForm):
    class Meta:
        model = User
        fields = ('name', 'douban_id', 'region', 'industry', 'phone_personal', 'phone_work', 'address', 'MSN', 'QQ', 'email', 'website', 'interests', 'honors', 'more') 

@login_validation
def edit_basic_info(request):
    if request.method == 'POST':  
        form = BasicInformationForm(request.POST, instance = User.objects.get(id = request.session['uid']))
        if form.is_valid():            
            form.save()
            return HttpResponseRedirect('/citylife/edit_basic_info/')
    else:  
        form = BasicInformationForm(instance = User.objects.get(id = request.session['uid']))
    
    return render_to_response('edit_basic_info.html', dict(form = form))

class WorkingPositionForm(ModelForm):
    class Meta:
        model = WorkingPosition
        widgets = {
            'time_from': forms.DateInput(format = '%Y-%m-%d'),
            'time_to': forms.DateInput(format = '%Y-%m-%d'),
        }

@login_validation
def add_working_position(request):
    if request.method == 'POST':  
        form = WorkingPositionForm(request.POST)
        if form.is_valid(): 
            wp = WorkingPosition(
                    company_name = form.cleaned_data['company_name'],
                    title = form.cleaned_data['title'],
                    time_from = form.cleaned_data['time_from'],
                    time_to = form.cleaned_data['time_to'],
                    desc = form.cleaned_data['desc'],
                    )
            wp.save()
            user = User.objects.get(id = request.session['uid'])
            user.working_position_ids.append(wp.id)
            user.save()
            return HttpResponseRedirect('/citylife/add_working_position/')
    else:  
        form = WorkingPositionForm()
    return render_to_response('add_working_position.html', dict(form = form))

class EducationRecordForm(ModelForm):
    class Meta:
        model = EducationRecord

@login_validation
def add_education_record(request):
    if request.method == 'POST':  
        form = EducationRecordForm(request.POST)
        if form.is_valid(): 
            school_name = form.cleaned_data.get('school_name')
            degree = form.cleaned_data.get('degree')
            major = form.cleaned_data.get('major')
            time_from = form.cleaned_data.get('time_from')
            time_to = form.cleaned_data.get('time_to')

            er = EducationRecord(school_name = school_name, degree = degree, major = major, time_from = time_from, time_to = time_to)
            er.save()
            
            user = User.objects.get(id = request.session['uid'])
            user.education_record_ids.append(er.id)
            user.save()
            return HttpResponseRedirect('/citylife/add_education_record/')
    else:  
        form = EducationRecordForm()
        return render_to_response('add_education_record.html', dict(form = form))

@login_validation
def profile(request):
    paras = dict(
            basic_info = User.objects.get(id = request.session['uid']),
            working_positions = WorkingPosition.objects.all(),
            education_records = EducationRecord.objects.all(),
            )
    print paras
    return render_to_response('profile.html', paras)    
    
##### experience #####

##### profile #####

class RegisterForm(forms.Form):
    email = forms.EmailField(initial = 'security.xling@gmail.com')
    psd = forms.CharField(max_length = 128, initial='11111111')
    name = forms.CharField(max_length = 128, initial='pseudoking')

def register(request):
    if request.method == 'POST':  
        form = RegisterForm(request.POST, request.FILES)  

        if form.is_valid(): 
            photo_upload = request.FILES.get('photo_upload')
            if photo_upload:
                handle_uploaded_image(photo_upload)

            email = form.cleaned_data['email']
            name = form.cleaned_data['name']
            psd = form.cleaned_data['psd']

            user = User(name = name, psd = psd, email = email)
            user.save()
            request.session['uid'] = user.id
            
            return HttpResponseRedirect('/citylife/home')
    else:
        form = RegisterForm()
        return render_to_response('register.html', dict(form = form))

